在前面 10 天,我們已經將 WebAuthn 前後端設計完成了,接下來就是要進行前後端串接了
會分成好幾天來分享串接過程中遇到的問題點,以及前面設計時,沒有考慮到或是有寫錯的地方
開始我們的串接之旅吧~
在開始串接前,要先記得將後端的 WebAuthn RP Server、ngrok 跟 PostgreSQL 資料庫 run 起來
可以透過 Visual Studio Code 的偵錯功能或是在 Terminal 輸入下面指令
go run main.go
WebAuthn RP Server run 起來之後,接著使用 ngrok 取得一個免費公開的測試用 domain
ngrok http 8080 # 8080 換成在 route.go 中設定的 port 號
PostgreSQL 資料庫我們是使用 Docker Compose 執行的,所以就使用 docker compose 的指令來執行即可
docker compose up -d # -d 是為了讓他在背景跑,不會阻塞住當前 Terminal
將 Signing & Capability 中的 Associated Domains 設為從 ngrok 取得的 domain,像是下面這樣
RequestConfiguration.Host
中 rpServer 的值接著將 RequestConfiguration.Host
中的 rpServer
設為從 ngrok 取得的 domain
extension RequestConfiguration {
// ...
enum Host: String {
case rpServer = "<設為從 ngrok 取得的 domain>"
}
// ...
}
從錯誤來看是找不到 excludeCredentials
,我們從後端來看的話,會發現是因為資料庫中,沒有已經完成註冊驗證的 Credential,所以會找不到需要排除的 Credential,也就沒有出現在後端回傳的 response body 中
那這個錯誤,我們可以從前端的 Response body 來下手,看到 AttestationOptionsResponse
這個 struct,裡面有一個 excludeCredentials
欄位,我們在型別那邊加上 ?
讓他變成是一個 Optional 的欄位,像是下面這樣
struct AttestationOptionsResponse: Decodable {
let status: String
let errorMessage: String
let rp: RelyingParty
let user: UserEntity
let challenge: String
let pubKeyCredParams: [PubKeyCredParam]
let timeout: Int
let excludeCredentials: [ExcludeCredential]? // 這邊加上 ? 讓他變成是 Optional 的
let authenticatorSelection: AuthenticatorSelectionCriteria
let attestation: String
// ...
}
修改完之後,原本的錯誤已經解決了,但又遇到新的錯誤了
從錯誤來看,這次是在 authenticatorSelection
欄位中找不到 authenticatorAttachment
欄位
從後端的 Response 來看,確實是沒有 authenticatorSelection
這個欄位的
那這個我們可以從後端來處理
首先,我們看到 api/attestation.go
中的 CredentialCreationOptionsRequest
將 AuthenticatorSelection
從原先定義的 map[string]interface{}
更改為 protocol.AuthenticatorSelection
type CredentialCreationOptionsRequest struct {
Username string `json:"username"`
DisplayName string `json:"displayName"`
AuthenticatorSelection protocol.AuthenticatorSelection `json:"authenticatorSelection,omitempty"`
Attestation string `json:"attestation"`
}
接著再看到 controller/attestation.go
的 StartAttestationHandler
我們多設定一個 go-webauthn 的 Registration Option,叫做 authenticatorSelectionOption
authenticatorSelectionOption := goWebAuthn.WithAuthenticatorSelection(request.AuthenticatorSelection)
接著在呼叫 BeginRegistration
的時候傳入
options, sessionData, err := webauthn.WebAuthn.BeginRegistration(user, excludeCredentialsOption, authenticatorSelectionOption)
修改完之後,原本的錯誤已經解決了,但接著又遇到新的錯誤了
從錯誤來看,這次是找不到 attachment
欄位
從後端的 Response 來看,確實是沒有 attachment
這個欄位的
那這個我們一樣可以從後端進行處裡,同樣看到 StartAttestationHandler
我們多設定一個 go-webauthn 的 Registration Option,叫做 attachmentOption
attestationOption := goWebAuthn.WithConveyancePreference(protocol.ConveyancePreference(request.Attestation))
接著在呼叫 BeginRegistration
的時候傳入
options, sessionData, err := webauthn.WebAuthn.BeginRegistration(
user,
excludeCredentialsOption,
authenticatorSelectionOption,
attestationOption,
)
在上面寫了三個 go-webauthn 的 Registration Option,那有沒有辦法可以合在一起,不用分開寫呢?
答案是有的!
我們只要改成像下面這樣宣告,並將原先的三個 Registration Option 在這邊一起進行設定,就可以了
registrationOptions := func (options *protocol.PublicKeyCredentialCreationOptions) {
options.CredentialExcludeList = user.CredentialExcludeList()
options.AuthenticatorSelection = request.AuthenticatorSelection
options.Attestation = protocol.ConveyancePreference(request.Attestation)
}
然後一樣在 BeginRegistration
傳入
options, sessionData, err := webauthn.WebAuthn.BeginRegistration(user, registrationOptions)
為什麼可以這樣改寫呢~這是因為 go-webauthn 的 Registration Option 本身就是一個 typealias
從 go-webauthn 的 source code 中可以看到
type RegistrationOption func(*protocol.PublicKeyCredentialCreationOptions)
今天通過了第一關,已經可以與 Apple Passkeys API 進行互動了,但後面還存在著未知挑戰
我們明天繼續勇闖第二關!